package cn.edu.xmu.oomall.customer.service;

import cn.edu.xmu.oomall.core.util.Common;
import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.oomall.customer.dao.CouponDao;
import cn.edu.xmu.oomall.customer.microservice.CouponActivityService;
import cn.edu.xmu.oomall.customer.microservice.vo.CouponActivityVoInfo;
import cn.edu.xmu.oomall.customer.model.bo.Coupon;
import cn.edu.xmu.oomall.customer.model.po.CouponPo;
import cn.edu.xmu.oomall.customer.model.po.CouponPoExample;
import cn.edu.xmu.oomall.customer.model.vo.*;
import cn.edu.xmu.privilegegateway.annotation.util.InternalReturnObject;
import cn.edu.xmu.privilegegateway.annotation.util.RedisUtil;
import cn.edu.xmu.privilegegateway.annotation.util.bloom.BloomFilter;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.edu.xmu.privilegegateway.annotation.util.Common.cloneVo;
import static cn.edu.xmu.privilegegateway.annotation.util.Common.genSeqNum;
import static cn.edu.xmu.privilegegateway.annotation.util.Common.setPoCreatedFields;

/**
 * @author Jianxiang Xie
 * @date 2021-12-9 20:12:57
 * @modifiedBy Jiazhe Yuan
 * @date 2021-12-20 00:30:01
 * @modify setNewCouponInfo方法的genSeqNum
 * @modifiedBy Jiazhe Yuan
 * @time 2021-12-23 11:31:36
 * @info 对接oomall-additional
 */

@Service
public class CouponService implements InitializingBean {

    @Autowired
    CouponDao couponDao;

    @Autowired
    CouponActivityService couponActivityService;

    @Autowired(required = false)
    BloomFilter<String> bloomFilter;

    @Autowired
    RedisUtil redisUtil;

    private final String COUPONFILTER = "couponFilter";
    private final String COUPONFILTERKEY = "actid_%d_customerId_%d";

    @Value("${oomall.activity.expiretime}")
    private long ActivityExpireTime;

    //活动对应的key
    private final String COUPONACTKEY = "actid_%d";

    @Value("${customer.bloomfilter.coupon.error-rate}")
    private double couponError = 0.01;
    @Value("${customer.bloomfilter.coupon.capacity}")
    private int couponCapacity = 10000;


    @Override
    public void afterPropertiesSet() throws Exception {

        if (!bloomFilter.checkFilter(COUPONFILTER)) {
            bloomFilter.newFilter(COUPONFILTER, couponError, couponCapacity);
        }
    }

    /**
     * 用于测试时重置redisbloom
     */
    public ReturnObject delFilter() {
        return new ReturnObject(bloomFilter.deleteFilter(COUPONFILTER));
    }

    /**
     * 用户获取自己的优惠券列表 分页
     *
     * @param loginUserId 用户id
     * @param state       优惠券状态
     * @param page        页
     * @param pageSize    页大小
     * @return 优惠券分页
     */
    public ReturnObject getCoupons(Long loginUserId, Integer state, Integer page, Integer pageSize) {
        //通过用户id与优惠券状态获取优惠券
        CouponPoExample example = new CouponPoExample();
        CouponPoExample.Criteria criteria = example.createCriteria();
        criteria.andCustomerIdEqualTo(loginUserId);
        if (state != null) {
            criteria.andStateEqualTo(state.byteValue());
        }
        var ret = couponDao.getCouponsByExample(example, page, pageSize);
        if (!ret.getCode().equals(ReturnNo.OK)) {
            return ret;
        }
        List<Coupon> coupons = ((PageInfo<Coupon>) ret.getData()).getList();
        List<CouponRetVo> couponRetVos = new ArrayList<>();
        //将BO对象转换为Vo对象
        for (Coupon coupon : coupons) {
            try {
                InternalReturnObject<CouponActivityVoInfo> internalRet = couponActivityService.getCouponActivityById(coupon.getActivityId());
                if (!internalRet.getErrno().equals(ReturnNo.OK.getCode())) {
                    return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, internalRet.getErrmsg());
                }
                //openFeign返回的是LinkedHashMap，用fastJson转化为对象
                CouponActivityVoInfo retAct = internalRet.getData();
                //SimpleActivityRetVo svo = cloneVo(retAct, SimpleActivityRetVo.class);
                CouponRetVo couponRetVo = cloneVo(coupon, CouponRetVo.class);
                //couponRetVo.setActivity(svo);
                couponRetVo.setActivityId(retAct.getId());
                couponRetVos.add(couponRetVo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        PageInfo pageInfo = PageInfo.of(coupons);
        pageInfo.setList(couponRetVos);
        return Common.getPageRetVo(new ReturnObject(pageInfo), CouponRetVo.class);
    }

    /**
     * 用户领取优惠券
     *
     * @param actId         活动id
     * @param loginUserId   用户id
     * @param loginUserName 用户名
     * @return 结果
     */
    public ReturnObject gainCoupon(Long actId, Long loginUserId, String loginUserName) {
        //判断是否已经领取过优惠券 返回633
        if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
            return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
        }
        //获取对应的活动
        //先在redis中查找
        CouponActivityVoInfo retAct = new CouponActivityVoInfo();
        String key = String.format(COUPONACTKEY, actId);
        retAct = (CouponActivityVoInfo) redisUtil.get(key);
        if (retAct == null) {
            InternalReturnObject<CouponActivityVoInfo> internalRet = null;
            try {
                internalRet = couponActivityService.getCouponActivityById(actId);
                //System.out.println("=============== here 3 —————————————————————— ");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            //System.out.println(JacksonUtil.toJson(internalRet));
            if (!internalRet.getErrno().equals(ReturnNo.OK.getCode())) {
                return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, internalRet.getErrmsg());
            }
            //System.out.println(JacksonUtil.toJson(internalRet.getData()));
            try {
                retAct = internalRet.getData();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            //活动不需要邀优惠券
            if (retAct.getQuantity() == -1) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, ReturnNo.RESOURCE_ID_NOTEXIST.getMessage());
            }
            //现在还未到开始领券时间 返回630
            if (LocalDateTime.now().isBefore(retAct.getCouponTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime())) {
                return new ReturnObject(ReturnNo.COUPON_NOTBEGIN, ReturnNo.COUPON_NOTBEGIN.getMessage());
            }
            //优惠活动终止 返回632
            if (LocalDateTime.now().isAfter(retAct.getEndTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime())) {
                return new ReturnObject(ReturnNo.COUPON_END, ReturnNo.COUPON_END.getMessage());
            }
            //活动不为上线状态
            if (retAct.getState() != 1) {
                return new ReturnObject(ReturnNo.STATENOTALLOW, "活动已经下线");
            }
            //活动的优惠券数量为0
            if (retAct.getQuantity() == 0) {
                return new ReturnObject(ReturnNo.COUPON_FINISH, ReturnNo.COUPON_FINISH.getMessage());
            }
            redisUtil.set(key, retAct, ActivityExpireTime);
        }
        //System.out.println("=============== here 4 —————————————————————— ");

        //System.out.println("————————————here 5 ————————————————————");

        int quantityType = retAct.getQuantityType();
        List<CouponGainRetVo> retVoList = new ArrayList<>();
        Coupon reUseCoupon = new Coupon();

        if (quantityType == 1) {
//            1.1.7之后quantityType的活动不控制每个用户只能领取一张
//                    if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
//                        return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
//                    }
//                    //将此用户的信息加入布隆过滤器中
//                    bloomFilter.addValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId));
            //总数控制
            //减少优惠券总数
            var daoRet1 = couponDao.decreaseCouponQuantity(actId, retAct.getNumKey(), 1, retAct.getQuantity());
            if (!daoRet1.getCode().equals(ReturnNo.OK)) {
                return daoRet1;
            }
            //设置新优惠券信息并加入数据库
            reUseCoupon = setNewCouponInfo(reUseCoupon, retAct, loginUserId, loginUserName);
//            var daoRet2 = couponDao.insertCoupon(reUseCoupon);
//            //插入成功且扣库存成功
//            if (!daoRet2.getCode().equals(ReturnNo.OK)) {
//                return daoRet2;
//            }
            couponDao.insertCoupon(reUseCoupon);
            retVoList.add(createCouponGainRetVo(reUseCoupon));
        } else if (quantityType == 0) {
            //每人数量
            try {
                synchronized (this) {
                    if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
                        return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
                    }
                    //将此用户的信息加入布隆过滤器中
                    bloomFilter.addValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId));
                }
                //生成n张优惠券，加入到数据库表中
                //将优惠券一次性发放完
                for (int i = 0; i < retAct.getQuantity(); i++) {
                    //设置优惠券的相关信息，插入数据库
                    reUseCoupon = setNewCouponInfo(reUseCoupon, retAct, loginUserId, loginUserName);
                    ReturnObject returnObject = couponDao.insertCoupon(reUseCoupon);
                    if (!returnObject.getCode().equals(ReturnNo.OK)) {
                        return new ReturnObject(returnObject.getCode(), "领取优惠券失败");
                    }
                    retVoList.add(createCouponGainRetVo(reUseCoupon));
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

//
//        //System.out.println("=============== here 6 —————————————————————— ");
//        //System.out.println("retVoList" + retVoList);
//        return new ReturnObject(retVoList);

        // ——————————————————————————————————————————————————————————————————————————————————————

        //判断是否已经领取过优惠券 返回633
//        if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
//            return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
//        }
//        //获取对应的活动
//        //先在redis中查找
//        CouponActivityVoInfo retAct = new CouponActivityVoInfo();
//        String key = String.format(COUPONACTKEY, actId);
//        retAct = (CouponActivityVoInfo) redisUtil.get(key);
//        if (retAct == null) {
//            InternalReturnObject<CouponActivityVoInfo> internalRet = null;
//            try {
//                internalRet = couponActivityService.getCouponActivityById(actId);
//                //System.out.println("=============== here 3 —————————————————————— ");
//            } catch (Exception e) {
//                System.out.println(e.getMessage());
//            }
//            //System.out.println(JacksonUtil.toJson(internalRet));
//            if (!internalRet.getErrno().equals(ReturnNo.OK.getCode())) {
//                return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, internalRet.getErrmsg());
//            }
//            //System.out.println(JacksonUtil.toJson(internalRet.getData()));
//            try {
//                retAct = internalRet.getData();
//            } catch (Exception e) {
//                System.out.println(e.getMessage());
//            }
//            redisUtil.set(key,retAct,ActivityExpireTime);
//        }
//        //System.out.println("=============== here 4 —————————————————————— ");
//        //表示该优惠活动不需要优惠卷，返回504错误
//        if (retAct.getQuantity() == -1) {
//            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, ReturnNo.RESOURCE_ID_NOTEXIST.getMessage());
//        }
//        //现在还未到开始领券时间 返回630
//        if (LocalDateTime.now().isBefore(retAct.getCouponTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime())) {
//            return new ReturnObject(ReturnNo.COUPON_NOTBEGIN, ReturnNo.COUPON_NOTBEGIN.getMessage());
//        }
//        //优惠活动终止 返回632
//        if (LocalDateTime.now().isAfter(retAct.getEndTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime())) {
//            return new ReturnObject(ReturnNo.COUPON_END, ReturnNo.COUPON_END.getMessage());
//        }
//        //活动不为上线状态
//        if (retAct.getState() != 1) {
//            return new ReturnObject(ReturnNo.STATENOTALLOW, "活动已经下线");
//        }
//        if (retAct.getQuantity() == 0) {
//            return new ReturnObject(ReturnNo.COUPON_FINISH, ReturnNo.COUPON_END.getMessage());
//        }
//
//        //System.out.println("————————————here 5 ————————————————————");
//
//        int quantityType = retAct.getQuantityType();
//        //为了提升效率少用cloneVo而直接用CouponPo
//        List<CouponGainRetVo> retVoList = new ArrayList<>();

//        //每人数量
//        synchronized (this) {
//            try {
//                if (quantityType == 0) {
//                    if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
//                        return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
//                    }
//                    bloomFilter.addValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId));
//                    //生成n张优惠券，加入到数据库表中
//                    //将优惠券一次性发放完
//                    for (int i = 0; i < retAct.getQuantity(); i++) {
//                        //设置优惠券的相关信息，插入数据库
//                        CouponPo couponPo = setNewCouponInfo(retAct, loginUserId, loginUserName);
//                        ReturnObject returnObject = couponDao.insertCoupon(couponPo);
//                        if (!returnObject.getCode().equals(ReturnNo.OK)) {
//                            return new ReturnObject(returnObject.getCode(), "领取优惠券失败");
//                        }
//                        retVoList.add(createCouponGainRetVo(couponPo));
//                    }
//                    //将此用户的信息加入布隆过滤器中
//                } else if (quantityType == 1) {
//                    if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
//                        return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
//                    }
//                    //将此用户的信息加入布隆过滤器中
//                    bloomFilter.addValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId));
//                    //总数控制
//                    //减少优惠券总数
//                    var daoRet1 = couponDao.decreaseCouponQuantity(actId, retAct.getNumKey(), 1, retAct.getQuantity());
//                    //将优惠券信息加入数据库
//                    CouponPo couponPo = setNewCouponInfo(retAct, loginUserId, loginUserName);
//                    ReturnObject daoRet2 = couponDao.insertCoupon(couponPo);
//                    //插入成功且扣库存成功
//                    if (daoRet1.getCode().equals(ReturnNo.OK) && daoRet2.getCode().equals(ReturnNo.OK)) {
//                        retVoList.add(createCouponGainRetVo(couponPo));
//                    } else {
//                        return daoRet1;
//                    }
//                }
//            } catch (Exception e) {
//                System.out.println(e.getMessage());
//            }
//        }
//        //每人数量
//        synchronized (this) {
//            if (bloomFilter.checkValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId))) {
//                return new ReturnObject(ReturnNo.COUPON_EXIST, "已经领取过本活动的优惠券");
//            }
//            //将此用户的信息加入布隆过滤器中
//            bloomFilter.addValue(COUPONFILTER, String.format(COUPONFILTERKEY, actId, loginUserId));
//        }
//        try {
//            if (quantityType == 0) {
//                //生成n张优惠券，加入到数据库表中
//                //将优惠券一次性发放完
//                for (int i = 0; i < retAct.getQuantity(); i++) {
//                    //设置优惠券的相关信息，插入数据库
//                    Coupon coupon = setNewCouponInfo(retAct, loginUserId, loginUserName);
//                    ReturnObject returnObject = couponDao.insertCoupon(coupon);
//                    if (!returnObject.getCode().equals(ReturnNo.OK)) {
//                        return new ReturnObject(returnObject.getCode(), "领取优惠券失败");
//                    }
//                    retVoList.add(createCouponGainRetVo(coupon));
//                }
//            } else if (quantityType == 1) {
//                //总数控制
//                //减少优惠券总数
//                var daoRet1 = couponDao.decreaseCouponQuantity(actId, retAct.getNumKey(), 1, retAct.getQuantity());
//                //将优惠券信息加入数据库
//                Coupon coupon = setNewCouponInfo(retAct, loginUserId, loginUserName);
//                ReturnObject daoRet2 = couponDao.insertCoupon(coupon);
//                //插入成功且扣库存成功
//                if (daoRet1.getCode().equals(ReturnNo.OK) && daoRet2.getCode().equals(ReturnNo.OK)) {
//                    retVoList.add(createCouponGainRetVo(coupon));
//                } else {
//                    return daoRet1;
//                }
//            }
//        } catch (Exception e) {
//            System.out.println(e.getMessage());
//        }
        //System.out.println("=============== here 6 —————————————————————— ");
        //System.out.println("retVoList" + retVoList);
        return new ReturnObject(retVoList);
    }

    /**
     * 为新优惠券设置属性
     *
     * @param retAct        活动对象
     * @param loginUserId   用户id
     * @param loginUserName 用户名
     * @return 修改后的Coupon对象
     */
    private Coupon setNewCouponInfo(Coupon coupon, CouponActivityVoInfo retAct, Long loginUserId, String loginUserName) {
        coupon.setId(0L);
        coupon.setCouponSn(genSeqNum(1));
        if (retAct.getValidTerm() == 0) {
            coupon.setBeginTime(retAct.getBeginTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
            coupon.setEndTime(retAct.getEndTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        } else {
            //优惠券的有效时间为当前时间加上ValidTerm
            //具体为活动结束时间 和 现在时间加上天数 的较小者
            coupon.setBeginTime(LocalDateTime.now());
            coupon.setEndTime(
                    LocalDateTime.now().plusDays(retAct.getValidTerm()).isAfter(retAct.getEndTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()) ?
                            retAct.getEndTime().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() : LocalDateTime.now().plusDays(retAct.getValidTerm())
            );
        }
        coupon.setCustomerId(loginUserId);
        coupon.setActivityId(retAct.getId());
        coupon.setName(retAct.getName() + "优惠券");
        coupon.setState(Coupon.State.RECEIVED.getCode().byteValue());
        coupon.setQuantityType(retAct.getQuantityType());
        setPoCreatedFields(coupon, loginUserId, loginUserName);
        return coupon;
    }

    /**
     * 利用Coupon对象得到CouponAllRetVo对象
     *
     * @param coupon CouponPo对象
     * @return CouponAllRetVo
     */
    private CouponGainRetVo createCouponGainRetVo(Coupon coupon) {
        CouponGainRetVo couponGainRetVo = new CouponGainRetVo();
        couponGainRetVo.setActivityId(coupon.getActivityId());
        couponGainRetVo.setCustomerId(coupon.getCustomerId());
        //不检查的属性，可删除
        //couponGainRetVo.setName(coupon.getName());
        //不检查的属性，可删除
        //couponGainRetVo.setCouponSn(coupon.getCouponSn());
        couponGainRetVo.setBeginTime(coupon.getBeginTime().atZone(ZoneId.systemDefault()));
        couponGainRetVo.setEndTime(coupon.getEndTime().atZone(ZoneId.systemDefault()));

        return couponGainRetVo;
    }

    public ReturnObject getCouponStates() {
        List<Map<String, Object>> stateList = new ArrayList<>();
        for (Coupon.State state : Coupon.State.values()) {
            Map<String, Object> tmp = new HashMap<>(16);
            tmp.put("code", state.getCode());
            tmp.put("name", state.getDescription());
            stateList.add(tmp);
        }
        return new ReturnObject(stateList);
    }
}